/*
 * Dijjer - A Peer to Peer HTTP Cache
 * Copyright (C) 2004,2005 Change.Tv, Inc
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package dijjer.util.logging;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class Logger {

	public static final String VERSION = "$Id: Logger.java 2 2008-03-21 14:33:03Z ian.clarke $";

	private static final String UNKNOWN_ERROR = "Unknown Error";
	private static final PrintWriter _out = new PrintWriter(System.out);
	private static final PrintWriter _err = new PrintWriter(System.err);

	public static Map _loggers = new HashMap();
	private static boolean _configured = false;

	private static LogHandler _defaultErrorLogger = new DefaultLogHandler(new PrintWriter(System.err), true, true, true);
	private static LogHandler _defaultDebugLogger = new DefaultLogHandler(new PrintWriter(System.out), true, true, true);
	private static LogHandler _defaultLogger = new DefaultLogHandler(new PrintWriter(System.out), true, false, false);
	private static LogHandler _defaultOutputLogger = new DefaultLogHandler(new PrintWriter(System.out), false, false, false);

	static {
		for (int i = 0; i < Level.values().length; i++) {
			Level lv = Level.values()[i];
			Map map = new HashMap();
			_loggers.put(lv, map);
			if (lv == Level.DEBUG) {
				addHandler(lv, null, _defaultDebugLogger);
			} else if (lv == Level.OUTPUT || lv == Level.INIT) {
				addHandler(lv, null, _defaultOutputLogger);
			} else if (lv.isError()) {
				addHandler(lv, null, _defaultErrorLogger);
			} else {
				addHandler(lv, null, _defaultLogger);
			}
		}
	}

	private Logger() {}

	public static void activate(Level level) {
		level.setActive(true);
	}

	public static void deactivate(Level level) {
		level.setActive(false);
	}

	public static void activate(String logger) {
		for (int i = 0; i < Level.values().length; i++) {
			Level level = Level.values()[i];
			activate(level, logger);
		}
	}

	public static void deactivate(String logger) {
		for (int i = 0; i < Level.values().length; i++) {
			Level level = Level.values()[i];
			deactivate(level, logger);
		}
	}

	public static void activate(Level level, String logger) {
		setHandlerActivity(level, logger, true);
	}

	public static void deactivate(Level level, String logger) {
		setHandlerActivity(level, logger, false);
	}

	private static void setHandlerActivity(Level level, String logger, boolean active) {
		List loggers = (List)((Map)_loggers.get(level)).get(logger);
		if (loggers != null) {
			for (Iterator i = loggers.iterator();i.hasNext();) {
				LogHandler logHandler = (LogHandler) i.next();
				logHandler.setActive(active);
			}
		}
	}

	public static void addHandler(Level level, String name, LogHandler handler) {
		List handlers = (List) ((Map) _loggers.get(level)).get(name);
		if (handlers == null) {
			handlers = new ArrayList();
			((Map) _loggers.get(level)).put(name, handlers);
		}
		handlers.add(handler);
	}

	public static void clearHandlers() {
		for (Iterator i = _loggers.values().iterator(); i.hasNext();) {
			((Map) i.next()).clear();
		}
	}

	public static void out(String outLine) {
		log(Level.OUTPUT, outLine, null);
	}

	public static void debug(String message) {
		log(Level.DEBUG, message, null);
	}

	public static void debug(String message, Object obj) {
		log(Level.DEBUG, message + "\n" + obj, null);
	}

	public static void init(String message) {
		log(Level.INIT, message, null);
	}

	public static void info(String message) {
		log(Level.INFO, message, null);
	}

	public static void warning(String message) {
		warning(message, null);
	}

	public static void warning(String message, Throwable thrown) {
		log(Level.WARNING, message, thrown);
	}

	public static void error(Throwable thrown) {
		error(UNKNOWN_ERROR, thrown);
	}

	public static void error(String message) {
		error(message, null);
	}

	public static void error(String message, Throwable thrown) {
		log(Level.ERROR, message, thrown);
	}

	public static void fatal(int exitCode, Throwable thrown) {
		fatal(exitCode, UNKNOWN_ERROR, thrown);
	}

	public static void fatal(int exitCode, String message) {
		fatal(exitCode, message, null);
	}

	public static void fatal(int exitCode, String message, Throwable thrown) {
		log(Level.FATAL, message, thrown);
		System.exit(exitCode);
	}

	public static void log(Level level, String message, Throwable thrown) {
		if (!level.isActive()) {
			return;
		}

		Date time = new Date();
		String sourceClass = null;
		String sourceMethod = null;
		int sourceLine = 0;

		StackTraceElement[] el = new Throwable().getStackTrace();
		for (int i = el.length - 1; i >= 0; i--) {
			if (el[i].getClassName().equals(Logger.class.getName())) {
				StackTraceElement e = el[i + 1];
				sourceClass = e.getClassName();
				sourceMethod = e.getMethodName();
				sourceLine = e.getLineNumber();
				break;
			}
		}
		List handlers = getHandlers(level, sourceClass);
		if (handlers != null) {
			for (Iterator i = handlers.iterator();i.hasNext();) {
				LogHandler logHandler = (LogHandler) i.next();
				if (logHandler.isActive()) {
					logHandler.log(level, time, message, sourceClass, sourceMethod, sourceLine, thrown);
				}
			}
		}
	}

	private static List getHandlers(Level level, String sourceClass) {
		List _handlers = (List)((Map)_loggers.get(level)).get(sourceClass);
		if (_handlers == null && sourceClass != null) {
			int lastDot = sourceClass.lastIndexOf('.');
			if (lastDot < 0) {
				return getHandlers(level, null);
			} else {
				return getHandlers(level, sourceClass.substring(0, lastDot));
			}
		} else if (_handlers == null) {
			return null;
		}
		return _handlers;
	}

	public static void configure(String configFile) throws Exception {
		File conf = new File(configFile);
		if (conf.exists()) {
			clearHandlers();
			loadConf(conf);
		} else {
			Logger.error(configFile + " does not exist, logging will be default and not be configured");
		}
	}

	private static void loadConf(File conf) throws Exception {
		BufferedReader reader = new BufferedReader(new FileReader(conf));
		String confLine = null;
		while ((confLine = reader.readLine()) != null) {
			if (confLine.startsWith("#") || confLine.startsWith("//")) {
				continue;
			}
			String[] params = confLine.split(",");
			if (params.length != 6) {
				continue;
			}
			String level = params[0].trim();
			String name = params[1].trim();
			if ("*".equals(name)) {
				name = null;
			}
			PrintWriter writer = null;
			String fileName = params[2].trim();
			if ("std:out".equalsIgnoreCase(fileName)) {
				writer = _out;
			} else if ("std:err".equalsIgnoreCase(fileName)) {
				writer = _err;
			} else {
				writer = new PrintWriter(new FileWriter(fileName, true));
			}
			boolean printLevel = Boolean.valueOf(params[3].trim().toLowerCase()).booleanValue();
			boolean printDate = Boolean.valueOf(params[4].trim().toLowerCase()).booleanValue();
			boolean printTrace = Boolean.valueOf(params[5].trim().toLowerCase()).booleanValue();
			addHandler(Level.getLevel(level), name, new DefaultLogHandler(writer, printLevel, printDate, printTrace));
		}
		reader.close();
	}

	public static void configure(String[] argStrings) throws Exception {
		if (_configured) {
			return;
		}
		_configured = true;
		List args = new ArrayList();
		String conf = null;
		for (int i = 0; i < argStrings.length; i++) {
			if (argStrings[i].startsWith("--logConf")) {
				conf = argStrings[i].substring(argStrings[i].indexOf('=') + 1);
			} else {
			    args.add(argStrings[i]);
			}
		}
		if (conf != null) {
			configure(conf);
		}
		if (args.contains("--debug")) {
			activate(Level.DEBUG);
		}
		if (args.contains("--nodebug")) {
			deactivate(Level.DEBUG);
		}
		if (args.contains("--init")) {
			activate(Level.INIT);
		}
		if (args.contains("--noinit")) {
			deactivate(Level.INIT);
		}
		if (args.contains("--info")) {
			activate(Level.INFO);
		}
		if (args.contains("--noinfo")) {
			deactivate(Level.INFO);
		}
		if (args.contains("--output")) {
			activate(Level.OUTPUT);
		}
		if (args.contains("--nooutput")) {
			deactivate(Level.OUTPUT);
		}
		if (args.contains("--warning")) {
			activate(Level.WARNING);
		}
		if (args.contains("--nowarning")) {
			deactivate(Level.WARNING);
		}
		if (args.contains("--error")) {
			activate(Level.ERROR);
		}
		if (args.contains("--noerror")) {
			deactivate(Level.ERROR);
		}
		if (args.contains("--fatal")) {
			activate(Level.FATAL);
		}
		if (args.contains("--nofatal")) {
			deactivate(Level.FATAL);
		}
	}

	public static String getCommString(int port, boolean incoming, Object remote, Object message) {
		StringBuffer sb = new StringBuffer();
		sb.append(System.currentTimeMillis() % 60000);
		sb.append(" ");
		sb.append(port);
		if (incoming) sb.append(" <- ");
		else          sb.append(" -> ");
		sb.append(remote.toString());
		sb.append(" : ");
		sb.append(message.toString());
		return sb.toString();
	}
}
